package com.lc.driver;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.util.Log;

import com.lc.protocol.IBaseDevice;
import com.lc.protocol.IFixedTransmitHandler;
import com.lc.protocol.ITransmitHandler;
import com.lc.tool.Convert;

import org.json.JSONObject;

public class HidTransmitHandler implements IBaseDevice, ITransmitHandler, IFixedTransmitHandler {

    public static final String ACTION_USB_PERMISSION = "com.lc.driver.USB_PERMISSION";
    public static final String LogTitle = "HID";

    public static final int USB_NONE = 0;
    public static final int USB_BLUK_TRANSMIT = 1;
    public static final int USB_CONTROL_TRANSMIT = 2;
    public static final int USB_INTERRUPT_TRANSMIT = 3;

    public static final int Ep0_MaxPacketSize = 64;

    protected Context _context = null;
    protected PendingIntent mPendingIntent = null;
    protected UsbManager _usbManager = null;
    protected UsbDevice _usbDevice = null;
    protected UsbInterface _usbInterface = null;
    protected UsbEndpoint _epIn = null;
    protected UsbEndpoint _epOut = null;
    protected UsbDeviceConnection _usbDeviceConnection = null;

    protected int _timeoutMs = 2000;
    protected int _vid = -1;
    protected int _pid = -1;
    protected int _inMode = USB_CONTROL_TRANSMIT;
    protected int _outMode = USB_CONTROL_TRANSMIT;

    // 接收的数据缓存
    protected String _cacheBuff = "";
    protected byte[] _sendBuff = null;
    protected byte[] _recvBuff = null;

    protected BroadcastReceiver _usbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Log.i("HID", "---> onReceive:" + action);
            if (ACTION_USB_PERMISSION.equals(action)) {
                Log.i(LogTitle, "Action:" + ACTION_USB_PERMISSION);

                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (device != null) {
                    if (intent.getBooleanExtra(
                            UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        Open(_vid, _pid, _timeoutMs);
                    }
                }
            }
            if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (device != null) {
                    Open(_vid, _pid, _timeoutMs);
                }
            }
            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                UsbDevice device = intent
                        .getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (_usbDevice != null && _usbDevice.equals(device)) {
                    Close();
                }
            }
        }
    };

    protected boolean getTheTargetDevice() {
        HashMap<String, UsbDevice> usbDeviceList = _usbManager.getDeviceList();
        Iterator<UsbDevice> usbDeviceIterator = usbDeviceList.values()
                .iterator();
        while (usbDeviceIterator.hasNext()) {
            UsbDevice device = usbDeviceIterator.next();
            String logMsg = device.getDeviceName() + " pid:" + device.getProductId() + " vid:" + device.getVendorId();
            Log.i(LogTitle, logMsg);

            if (_pid == device.getProductId() && _vid == device.getVendorId()) {
                _usbDevice = device;
                if (!_usbManager.hasPermission(_usbDevice)) {
                    Log.i(LogTitle, "----> requestPermission");
                    mPendingIntent = PendingIntent.getBroadcast(_context, 0,
                            new Intent(ACTION_USB_PERMISSION), 0);
                    _usbManager.requestPermission(_usbDevice, mPendingIntent);
                }
                return true;
            }
        }
        return false;
    }

    protected boolean commWithUsbDevice(int timeoutMs) {
        if (_usbInterface == null) {
            Log.e(LogTitle, "_usbInterface is null");
            return false;
        }

        UsbDeviceConnection conn = null;
        long tickCount = System.currentTimeMillis();
        do {
            if (_usbManager.hasPermission(_usbDevice)) {
                conn = _usbManager.openDevice(_usbDevice);
                Log.i(LogTitle, "hasPermission!");
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                Log.e(LogTitle, "Wait", e);
            }

            if((System.currentTimeMillis() - tickCount) > timeoutMs) {
                Log.e(LogTitle, "Wait Timeout");
                break;
            }
        } while (!_usbManager.hasPermission(_usbDevice));

        if (conn == null) {
            Log.e(LogTitle, "UsbDeviceConnection is null");
            return false;
        }
        if (conn.claimInterface(_usbInterface, true)) {
            _usbDeviceConnection = conn;

            _sendBuff = new byte[GetSendLength()];
            _recvBuff = new byte[GetRecvLength()];
            return true;
        }
        conn.close();
        Log.e(LogTitle, "claimInterface failed");
        return false;
    }

    public HidTransmitHandler(Context context) {
        _context = context;
        _usbManager = (UsbManager) _context.getSystemService(Context.USB_SERVICE);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_USB_PERMISSION);
        intentFilter.addAction("android.hardware.usb.action.USB_DEVICE_ATTACHED");
        intentFilter.addAction("android.hardware.usb.action.USB_DEVICE_DETACHED");
        _context.registerReceiver(_usbReceiver, intentFilter);
        Log.i(LogTitle, "registerReceiver");
    }

    public boolean Open(int vid, int pid, int timeoutMs) {
        _vid = vid;
        _pid = pid;
        _timeoutMs = timeoutMs;

        if(!getTheTargetDevice()) {
            Log.e(LogTitle, "getTheTargetDevice failed");
            return false;
        }

        for (int i = 0; i < _usbDevice.getInterfaceCount(); i++) {
            UsbInterface intf = _usbDevice.getInterface(i);
            if(intf.getInterfaceClass() == 3) {
                _usbInterface = intf;

                for(int j = 0;j < intf.getEndpointCount(); ++j) {
                    UsbEndpoint ep = intf.getEndpoint(j);
                    if(ep.getDirection() == 128) {
                        _epIn = ep;
                    } else {
                        _epOut = ep;
                    }
                }
                break;
            }
        }
        return commWithUsbDevice(timeoutMs);
    }

    @Override
    public boolean Open(JSONObject arg) {
        Log.i(LogTitle, "Arg:" + arg.toString());

        int vid = arg.optInt("VID");
        int pid = arg.optInt("PID");
        int timeoutMs = arg.optInt("Timeout", 5000);

        return Open(vid, pid, timeoutMs);
    }

    @Override
    public boolean IsOpen() {
        return _usbDeviceConnection != null;
    }

    @Override
    public void Close() {
        Log.i(LogTitle, "Close UsbDeviceConnection");
        if (_usbDeviceConnection != null) {
            Log.i(LogTitle, "releaseInterface");
            _usbDeviceConnection.releaseInterface(_usbInterface);
            _usbDeviceConnection.close();
        }
        _sendBuff = null;
        _recvBuff = null;
        _epIn = null;
        _epOut = null;
        _usbDeviceConnection = null;
    }

    @Override
    public boolean OnWrite(byte[] data, int datalen, int timeoutMs) {
        if(_usbDeviceConnection == null) {
            Log.e(LogTitle, "_usbDeviceConnection is null");
            return false;
        }

        int sendlen = 0;
        int pkglen = 0;
        while (sendlen < datalen) {
            pkglen = Math.min(datalen - sendlen, GetSendLength());
            Arrays.fill(_sendBuff, (byte) 0);
            System.arraycopy(data, sendlen, _sendBuff, 0, pkglen);

            int iret = 0;
            switch (_outMode) {
                case USB_CONTROL_TRANSMIT:
                    iret = _usbDeviceConnection.controlTransfer(0x21, 0x09,
                            0x300, _usbInterface.getId(), _sendBuff, _sendBuff.length, timeoutMs);
                    break;
                case USB_BLUK_TRANSMIT:
                    iret = _usbDeviceConnection.bulkTransfer(_epOut, _sendBuff, _sendBuff.length, timeoutMs);
                    break;
            }

            String str = Convert.HexToString(_sendBuff, pkglen);
            Log.i(LogTitle, "写入数据:[" + pkglen + "]");
            Log.i(LogTitle, str);

            if(iret < _sendBuff.length) {
                Log.e(LogTitle, "Transfer:" + iret);
                return false;
            }
            sendlen += pkglen;
        }
        return true;
    }

    @Override
    public boolean OnRead(byte[] data, int[] datalen, int timeoutMs) {
        // 还有未接收完的缓存数据
        if(_cacheBuff.length() > 0) {
            int offset = 2 * data.length;

            String rData = _cacheBuff.substring(0, offset);
            byte[] rBuff = Convert.StringToHex(rData);
            System.arraycopy(rBuff, 0, data, 0, data.length);
            datalen[0] = data.length;

            _cacheBuff = _cacheBuff.substring(offset);

            Log.i(LogTitle, "接收缓存数据:[" + data.length + "]");
            Log.i(LogTitle, rData);
            return true;
        }

        if(_usbDeviceConnection == null) {
            Log.e(LogTitle, "_usbDeviceConnection is null");
            return false;
        }
        Arrays.fill(_recvBuff, (byte) 0x00);

        int len = 0;
        switch (_inMode) {
            case USB_CONTROL_TRANSMIT:
                len = _usbDeviceConnection.controlTransfer(0xA1, 0x01, 0x300,
                        _usbInterface.getId(), _recvBuff, _recvBuff.length, timeoutMs);
                break;
            case USB_BLUK_TRANSMIT:
                len = _usbDeviceConnection.bulkTransfer(_epIn, _recvBuff, _recvBuff.length, timeoutMs);
                break;
        }
        if(len <= 0) {
            Log.e(LogTitle, "Transfer:" + len);
            return false;
        }

        String str = Convert.HexToString(_recvBuff, Math.min(len, 256));
        Log.i(LogTitle, "接收数据:[" + len + "]");
        Log.i(LogTitle, str);

        System.arraycopy(_recvBuff, 0, data, 0, len);
        datalen[0] = len;
        return true;
    }

    @Override
    public int GetSendLength() {
        return _epOut == null ? Ep0_MaxPacketSize : _epOut.getMaxPacketSize();
    }

    @Override
    public int GetRecvLength() {
        return _epIn == null ? Ep0_MaxPacketSize : _epIn.getMaxPacketSize();
    }
}

